package com.maxlen.admin.config;

import com.maxlen.admin.security.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

/**
 * Spring Security 配置
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private CustomAccessDeniedHandler accessDeniedHandler;

    @Autowired
    private CustomAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private CustomFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource;

    @Autowired
    private CustomAccessDecisionManager accessDecisionManager;

    @Autowired
    private CustomUserDetailServiceImpl myUserDetail;

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    // 自定义 DaoAuthenticationProvider 为了设置 hideUserNotFoundExceptions 为 false
    // 那么我们写的 UserDetailService 抛出的异常就不会被重新包装
    // 总结：为了让用户知道是用户名错误还是密码错误
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setHideUserNotFoundExceptions(false);
        provider.setUserDetailsService(myUserDetail);
        provider.setPasswordEncoder(passwordEncoder());
        return provider;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 添加后处理器为 FilterSecurityInterceptor 注入自定义权限判断处理
        http.authorizeRequests()
                        .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                            @Override
                            public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                                object.setAccessDecisionManager(accessDecisionManager);
                                object.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
                                return object;
                            }
                        });

        // 所有请求都要判断权限
        http.authorizeRequests().anyRequest().authenticated();

        // csrf
        http.csrf().disable().exceptionHandling()
                // 没有权限处理
                .accessDeniedHandler(accessDeniedHandler)
                // 未登录处理
                .authenticationEntryPoint(authenticationEntryPoint);

        // 退出登录
        http.logout()
                .logoutUrl("/admin/logout")
                .logoutSuccessHandler(new CustomLogoutSuccessHandler(redisTemplate));

        // 登录
        http.formLogin()
                .loginProcessingUrl("/admin/login")
                .permitAll();

        // 基于 token,所以不需要 session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 添加自定义过滤器
        http.addFilterBefore(new CustomUsernamePasswordFilter(new AntPathRequestMatcher("/admin/login","POST"), authenticationManager(), redisTemplate), UsernamePasswordAuthenticationFilter.class).httpBasic();
        http.addFilterBefore(jwtAuthenticationTokenFilter, LogoutFilter.class);

        http.headers().cacheControl();
    }
}
